checkout: Add an option to require hardlinks
authorColin Walters <walters@verbum.org>
Sun, 26 Jun 2016 14:25:03 +0000 (10:25 -0400)
committerAtomic Bot <atomic-devel@projectatomic.io>
Mon, 27 Jun 2016 13:08:46 +0000 (13:08 +0000)
I've seen a few people hit this and wonder why checkouts are slow/take
space.  Really, ensuring this happens is the *point* of OSTree.
Physical copies should be a last resort fallback for very unusual
situations (one of those is rpm-ostree checking out the db since
librpm doesn't know how to read from libostree).

Even I hit the fact that `/var` is a mountpoint disallowing hardlinks
with `/ostree` once and was confused.  =)

Add this to the rofiles-fuse test case because it creates a mount
point.

Closes: #368
Approved by: jlebon

src/libostree/ostree-repo-checkout.c
src/libostree/ostree-repo.h
src/ostree/ot-builtin-checkout.c
tests/test-rofiles-fuse.sh

index a6d69c42f29d282227392978ecd8d6f590ea55f9..0094aefdf1915320f064e02b09b67b346c58f00b 100644 (file)
@@ -346,7 +346,7 @@ checkout_file_hardlink (OstreeRepo                          *self,
  again:
   if (linkat (srcfd, loose_path, destination_dfd, destination_name, 0) != -1)
     ret_was_supported = TRUE;
-  else if (errno == EMLINK || errno == EXDEV || errno == EPERM)
+  else if (!options->no_copy_fallback && (errno == EMLINK || errno == EXDEV || errno == EPERM))
     {
       /* EMLINK, EXDEV and EPERM shouldn't be fatal; we just can't do the
        * optimization of hardlinking instead of copying.
index 2f60e633d43fa79d3001bf6405c429ddba93760a..872f9b813cb0ca750600dc44f616488c07b3a259 100644 (file)
@@ -750,7 +750,8 @@ typedef struct {
   guint enable_uncompressed_cache : 1;
   guint disable_fsync : 1;
   guint process_whiteouts : 1;
-  guint reserved : 29;
+  guint no_copy_fallback : 1;
+  guint reserved : 28;
 
   const char *subpath;
 
index 810a8f72c495899e53ce8fa4085eee3c432a6400..c6fdf1fc8a90c66d91f42bab84df3f204bb7257f 100644 (file)
@@ -40,6 +40,7 @@ static gboolean opt_whiteouts;
 static gboolean opt_from_stdin;
 static char *opt_from_file;
 static gboolean opt_disable_fsync;
+static gboolean opt_require_hardlinks;
 
 static gboolean
 parse_fsync_cb (const char  *option_name,
@@ -67,6 +68,7 @@ static GOptionEntry options[] = {
   { "from-stdin", 0, 0, G_OPTION_ARG_NONE, &opt_from_stdin, "Process many checkouts from standard input", NULL },
   { "from-file", 0, 0, G_OPTION_ARG_STRING, &opt_from_file, "Process many checkouts from input file", "FILE" },
   { "fsync", 0, 0, G_OPTION_ARG_CALLBACK, parse_fsync_cb, "Specify how to invoke fsync()", "POLICY" },
+  { "require-hardlinks", 'H', 0, G_OPTION_ARG_NONE, &opt_require_hardlinks, "Do not fall back to full copies if hardlinking fails", NULL },
   { NULL }
 };
 
@@ -85,7 +87,7 @@ process_one_checkout (OstreeRepo           *repo,
    * `ostree_repo_checkout_tree_at` until such time as we have a more
    * convenient infrastructure for testing C APIs with data.
    */
-  if (opt_disable_cache || opt_whiteouts)
+  if (opt_disable_cache || opt_whiteouts || opt_require_hardlinks)
     {
       OstreeRepoCheckoutOptions options = { 0, };
       
@@ -97,6 +99,7 @@ process_one_checkout (OstreeRepo           *repo,
         options.process_whiteouts = TRUE;
       if (subpath)
         options.subpath = subpath;
+      options.no_copy_fallback = opt_require_hardlinks;
 
       if (!ostree_repo_checkout_tree_at (repo, &options,
                                          AT_FDCWD, destination,
index d021df0980bfdc1b50505790b7f2c727d5e542a8..736747f73f8cb496ae611326f888cfa29a95af7c 100755 (executable)
@@ -26,7 +26,7 @@ skip_without_user_xattrs
 
 setup_test_repository "bare-user"
 
-echo "1..5"
+echo "1..6"
 
 mkdir mnt
 
@@ -71,3 +71,13 @@ echo "ok deletion"
 ${CMD_PREFIX} ostree --repo=repo commit -b test2 -s fromfuse --link-checkout-speedup --tree=dir=checkout-test2
 
 echo "ok commit"
+
+${CMD_PREFIX} ostree --repo=repo checkout -U test2 mnt/test2-checkout-copy-fallback
+assert_file_has_content mnt/test2-checkout-copy-fallback/anewfile-for-fuse anewfile-for-fuse
+
+if ${CMD_PREFIX} ostree --repo=repo checkout -UH test2 mnt/test2-checkout-copy-hardlinked 2>err.txt; then
+    assert_not_reached "Checking out via hardlinks across mountpoint unexpectedly succeeded!"
+fi
+assert_file_has_content err.txt "Invalid cross-device link"
+
+echo "ok checkout copy fallback"